/*
 * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.inspur.edp.web.npmpackage.core.npmlogin;

import com.inspur.edp.web.common.entity.NodeJsCommandEnum;
import com.inspur.edp.web.common.io.NodejsFunctionUtility;
import com.inspur.edp.web.common.logger.WebLogger;
import com.inspur.edp.web.common.utility.CommandLineUtility;
import com.inspur.edp.web.common.utility.StringUtility;
import com.inspur.edp.web.npmpackage.api.entity.NpmInstallParameter;
import com.inspur.edp.web.npmpackage.api.entity.NpmPackageResponse;
import org.apache.commons.lang3.SystemUtils;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;


/**
 * npm login 命令执行
 *
 * @author guozhiqi
 */
public class NpmLoginCommandExecutor {
    /**
     * npm 登录命令执行
     *
     * @param npmInstallParameter
     * @param currentServerPath
     */
    public static NpmPackageResponse execute(NpmInstallParameter npmInstallParameter, String currentServerPath) {
        // 构造install 命令参数 执行npm包安装操作
        String npmCommandExecuteWithPath = NodejsFunctionUtility.getNodeJsCommandInServerWithOS(NodeJsCommandEnum.Npm, currentServerPath);
        String args = npmCommandExecuteWithPath;

        args += " login ";

        String npmLoginCommand = NpmLoginCommandParameterGenerator.generate(npmInstallParameter);

        if (!StringUtility.isNullOrEmpty(npmLoginCommand)) {
            args += npmLoginCommand;
        }

        String[] command = {args};

        // 执行npm 包安装
        return runLoginCommand(command, npmInstallParameter.getUserName(), npmInstallParameter.getPassword(), npmInstallParameter.getEmail());

    }

    public static NpmPackageResponse runLoginCommand(String[] commands, String userName, String password, String email) {
        String command = String.join(" && ", commands);
        return runLoginCommand(command, true, userName, password, email);

    }

    /**
     * 执行命令
     *
     * @param command 待执行命令
     * @param isWait  是否等待返回结果
     */
    public static NpmPackageResponse runLoginCommand(String command, boolean isWait, String userName, String password, String email) {
        String errorMessage = "";
        BufferedReader bufferedReader = null;
        Process process = null;
        OutputStream outputStream = null;
        InputStream errinputStream = null;
        StringBuilder errorSB = new StringBuilder();
        try {
            WebLogger.Instance.info(command, NpmLoginCommandExecutor.class.getName());

            String updateCommand = command;
            if (SystemUtils.IS_OS_WINDOWS) {
                updateCommand = "cmd.exe" + " /C" + " " + command;
                process = Runtime.getRuntime().exec(updateCommand);
            } else if (SystemUtils.IS_OS_UNIX) {
                updateCommand = command;
                process = Runtime.getRuntime().exec(new String[]{"/bin/sh", "-c", updateCommand});
            }
            if (updateCommand == null) {
                return NpmPackageResponse.createError("不支持的操作系统类型，请联系开发人员处理");
            }

            InputStreamReader inputStreamReader = new InputStreamReader(process.getInputStream());
            bufferedReader = new BufferedReader(inputStreamReader);

            outputStream = process.getOutputStream();
            errinputStream = process.getErrorStream();
            InputStreamReader errInputStreamReader = new InputStreamReader(errinputStream);

            StringBuilder sb = new StringBuilder();

            int len = -1;
            char[] c = new char[1024];
            //读取进程输入流中的内容
            while ((len = inputStreamReader.read(c)) != -1) {
                String s = new String(c, 0, len);

                if (!StringUtility.isNullOrEmpty(s)) {
                    boolean canPrint = true;
                    if (s.trim().contains("Username:")) {
                        canPrint = false;
                        String strUserName = userName + "\n";
                        outputStream.write(strUserName.getBytes(StandardCharsets.UTF_8));
                        outputStream.flush();
                    } else if (s.trim().contains("Password:")) {
                        canPrint = false;
                        String strPassword = password + "\n";
                        outputStream.write(strPassword.getBytes(StandardCharsets.UTF_8));
                        outputStream.flush();
                    } else if (s.contains("Email:")) {
                        canPrint = false;
                        String strEmail = email + "\n";
                        outputStream.write(strEmail.getBytes(StandardCharsets.UTF_8));
                        outputStream.flush();
                        outputStream.close();
                    }
                    if (canPrint) {
                        // 输出日志信息
                        WebLogger.Instance.info(s, NpmLoginCommandExecutor.class.getName());
                    }
                }
                //判断是否存在错误描述
                if (CommandLineUtility.checkHasError(s)) {
                    if (s.contains("npm ERR! Unable to authenticate")) {
                        // npm登录失败
                        errorMessage = "npm登录失败，请查看网络是否正常连接";
                    } else {
                        errorMessage = s;
                    }
                    break;
                }
            }

            // 获取error输出流

            while ((len = errInputStreamReader.read(c)) != -1) {
                String s = new String(c, 0, len);
                errorSB.append(s);
                WebLogger.Instance.info(s, NpmLoginCommandExecutor.class.getName());
            }

            process.waitFor();

        } catch (Exception e) {
            WebLogger.Instance.error(e);
            WebLogger.Instance.info(Arrays.toString(e.getStackTrace()), NpmLoginCommandExecutor.class.getName());
            errorMessage = e.getMessage() + Arrays.toString(e.getStackTrace());
        } finally {
            if (bufferedReader != null) {
                try {
                    bufferedReader.close();
                } catch (Exception e) {
                    WebLogger.Instance.error(e);
                    errorMessage = e.getMessage() + Arrays.toString(e.getStackTrace());
                }
            }
        }
        // 如果出现编译错误 那么通过异常的方式将错误信息进行抛出
        if (!StringUtility.isNullOrEmpty(errorMessage)) {
            return NpmPackageResponse.createError("npm install executed failed!" + errorMessage);
        }

        if (!StringUtility.isNullOrEmpty(errorSB.toString())) {
            // 针对几种固定错误 进行提示
            String header = "然后重新执行更新操作。";
            String strErrorMessage = errorSB.toString();
            if (strErrorMessage.contains("network This is a problem related to network connectivity")) {
                errorMessage = "请检查Url地址是否正确或网络是否可以联通，" + header;
            } else if (strErrorMessage.contains("401 Unauthorized") && strErrorMessage.contains("Bad username or password")) {
                // 用户名或密码错误
                errorMessage = "用户名或密码错误,请修正后重新执行更新操作。";
            } else if (strErrorMessage.contains("404 Not Found - PUT") && strErrorMessage.contains("/user/org.couchdb.user")) {
                errorMessage = "请检查Url地址是否正确或用户名是否存在，请修正后重新执行更新操作。";
            } else {
                errorMessage = errorMessage + errorSB;
            }
        }

        if (!StringUtility.isNullOrEmpty(errorMessage) && errorSB.toString().contains("npm ERR!")) {
            return NpmPackageResponse.createError(errorMessage);
        }

        return NpmPackageResponse.create();
    }

}
